/*
 * Copyright ©2015-2021 Jaemon. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gitee.jaemon.spring.adorner.handler;

import io.gitee.jaemon.spring.adorner.core.AbstractSpringParamHandler;
import io.gitee.jaemon.spring.adorner.exception.AdornerException;
import io.gitee.jaemon.spring.adorner.type.ParamDefinition;
import io.gitee.jaemon.spring.adorner.util.AnnotationUtils;
import io.gitee.jaemon.spring.adorner.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

/**
 * 参数定义处理器
 *
 * @author Jaemon
 * @since 1.0
 */
public class ParamDefinitionHandler extends AbstractSpringParamHandler<List<ParamDefinition>> {

    @Override
    public List<ParamDefinition> requestHandler(Method method) throws AdornerException {
        List<ParamDefinition> request = new ArrayList<>();

        // 方法参数注解
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        // 获取方法参数的泛型参数类型
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        // 参数名称
        Parameter[] parameters = method.getParameters();
        Class<?>[] parameterTypes = method.getParameterTypes();

        Map<String, Object> params;
        Map<String, String> genericCache;

        // 遍历参数类型
        OK:
        for (int i = 0; i < parameterTypes.length; i++) {
            // 方法参数类型
            Class<?> parameterType = parameterTypes[i];
            Parameter parameter = parameters[i];
            params = new HashMap<>();
            request.add(ParamDefinition.of(parameterType, parameter, params));

            // 数据类型及以下参数类型不做处理
            boolean ignore = type(parameterType);
            if (ignore) {
                continue;
            }

            // 参数带泛型缓存
            genericCache = new HashMap<>();

            // 判断参数是否存在注定注解
            boolean exists = AnnotationUtils.containAnnotation(
                    parameterAnnotations[i], requestAnnotation()
            );
            if (!exists) {
                continue;
            }

            mainClazzHandler(parameterType, params, genericCache);

            // 当前字段不是泛型类型
            if (genericCache.isEmpty()) {
                continue OK;
            }

            // 泛型顺序, 如： <T, E>
            TypeVariable<? extends Class<?>>[] genericTypeParameters = parameterType.getTypeParameters();
            // 获取参数对应的泛型对象
            Type genericParameterType = genericParameterTypes[i];
            genericHandler(genericParameterType, genericTypeParameters, params, genericCache);
        }

        return request;
    }


    @Override
    public List<ParamDefinition> responseHandler(Method method) throws AdornerException {
        List<ParamDefinition> response = new ArrayList<>();
        Class<?> returnType = method.getReturnType();
        Map<String, Object> params = new HashMap<>();
        response.add(ParamDefinition.of(returnType, params));
        Map<String, String> genericCache = new HashMap<>();
        mainClazzHandler(returnType, params, genericCache);

        // 泛型顺序, 如： <T, E>
        TypeVariable<? extends Class<?>>[] typeParameters = returnType.getTypeParameters();
        Type genericReturnType = method.getGenericReturnType();
        genericHandler(genericReturnType, typeParameters, params, genericCache);

        return response;
    }


    /**
     * 主类型处理
     *
     * @param clazz
     *          类类型
     * @param params
     *          参数信息
     * @param genericCache
     *          泛型缓存映射
     */
    protected void mainClazzHandler(Class<?> clazz, Map<String, Object> params, Map<String, String> genericCache) {
        List<Field> fields = ClassUtils.fields(clazz);
        for (Field field : fields) {
            if (field.getType() != field.getGenericType()) {
                params.put(field.getName(), new HashMap<>());
                // 缓存当前泛型对应的字段名
                genericCache.put(field.getGenericType().getTypeName(), field.getName());
            } else if (type(field.getType())) {
                params.put(field.getName(), field);
            } else {
                // 自定义类型
                Map<String, Object> inner = new HashMap<>();
                params.put(field.getName(), inner);
                typeHandler(field.getType(), inner);
            }
        }
    }

    /**
     * 对泛型参数进行处理
     *
     * @param type
     *          类型
     * @param typeParameters
     *          泛型顺序
     * @param params
     *          参数信息
     * @param genericCache
     *          泛型缓存映射
     */
    protected void genericHandler(
            Type type,
            TypeVariable<? extends Class<?>>[] typeParameters,
            Map<String, Object> params,
            Map<String, String> genericCache
    ) {
        if (ParameterizedType.class.isInstance(type)) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            // 泛型的实际类型, 如： <String, Integer>
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            // 安全判断, 泛型参数个数对应不上
            if (genericCache.size() != actualTypeArguments.length) {
                return;
            }

            for (int i = 0; i < actualTypeArguments.length; i++) {
                Class<?> actualTypeArgument = (Class<?>) actualTypeArguments[i];
                String fieldName = genericCache.get(typeParameters[i].getName());
                if (params.containsKey(fieldName)) {
                    Object o = params.get(fieldName);
                    if (o instanceof Map) {
                        Map<String, Object> in = (Map<String, Object>) o;
                        List<Field> fields = ClassUtils.fields(actualTypeArgument);
                        for (Field field : fields) {
                            in.put(field.getName(), field);

                            if (type(field.getType())) {
                                in.put(field.getName(), field);
                            } else {
                                // 自定义类型
                                Map<String, Object> inner = new HashMap<>();
                                in.put(field.getName(), inner);
                                typeHandler(field.getType(), inner);
                            }
                        }
                    }
                }
            }
        }
    }


    /**
     * 类类型递归处理
     *
     * @param clazz
     *          类类型
     * @param params
     *          参数信息
     */
    protected void typeHandler(Class<?> clazz, Map<String, Object> params) {
        List<Field> fields = ClassUtils.fields(clazz);
        for (Field field : fields) {
            if (type(field.getType())) {
                params.put(field.getName(), field);
            } else {
                Map<String, Object> inner = new HashMap<>();
                params.put(field.getName(), inner);
                typeHandler(field.getType(), inner);
            }
        }
    }


    private boolean type(Class<?> clazz) {
        return clazz.getComponentType() != null || (
                clazz.isPrimitive() || Number.class.isAssignableFrom(clazz) ||
                        CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz) ||
                        Date.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz) ||
                        Map.class.isAssignableFrom(clazz) || Collection.class.isAssignableFrom(clazz)
        );
    }
}